/*->c.wild */

/* wildcard code added by David Pilling for the Archimedes */
/* bits taken from C Kermit */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "arthur.h"

#define MAXWLD 100                     /* Maximum wildcard filenames */
#define MAXNAMLEN 255

 
static int fcount;                     /* Number of files in wild group */
static char nambuf[MAXNAMLEN+2];       /* Buffer for a filename */
 
static char *mtchs[MAXWLD],            /* Matches found for filename */
     **mtchptr;                        /* Pointer to current match */

 
/*
 * osgbpb with r0==9 is used to read directory names;
 * the data returned is copied into a struct dirent.
 */
 
struct dirent {
       unsigned int d_reclen;               /* length of this record */
       unsigned int d_namlen;               /* length of string in d_name */
       char    d_name[MAXNAMLEN + 1];  /* name must be no longer than this */
};
 
/*
 * This macro calculates the amount of storage required for
 * the directory entry.
 */
#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &3))
 
/*
 * Definitions for library routines operating on directories.
 */
typedef struct _dirdesc
{
       long    dd_pos; /* current position in directory. */
       struct dirent *dd_dirent;
       char    dd_name[MAXNAMLEN+1]; /* osgbpb needs a name */
       char    dd_buf[MAXNAMLEN+1]; /* osgbpb needs a buffer */
} DIR;
 
extern DIR *opendir( char * );
extern void closedir( DIR *);

struct FileData {int loadaddr, execaddr, length, attrib;};
 
struct BTim {int low, high;}; 
 
int
GetFileInformation( info, tstamp, name, len )
struct FileData *info;
struct BTim *tstamp;
char * name;
int len;
{
static osfile_block o_b;
static reg_set rs;
error *ret;
 


    /* Open file for reading */
    rs.r[0] = 64;
    rs.r[1] = (int)name;
    if( (ret=osfind( &rs )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret->errmess,ret->errnum);
        return -1;
    }
 
    if( rs.r[0] == 0 )
        /* File does not exist */
        return -1;
     
    /* close the file just opened */
    rs.r[1] = rs.r[0];
    rs.r[0] = 0;
 
    if( (ret=osfind( &rs )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret->errmess,ret->errnum);
        return -1;
    }
 
    o_b.action = 5;
    o_b.name = name;
 
    if( (ret=osfile( &o_b )) != NULL )
    {
        fprintf(stderr,"%s (%d)\n",ret->errmess,ret->errnum);
        return -1;
    }
 
    info->loadaddr = o_b.loadaddr;
    info->execaddr = o_b.execaddr;
    info->length = o_b.start;
    info->attrib = o_b.end;
 
    len=0; /* filename len */
    tstamp->low=0;

    /* Return file type */
    return o_b.action;
}
 
DIR *
opendir( filename )
char *filename;
{
DIR * dirp;
 
        if( (dirp = (DIR*)malloc(sizeof(DIR))) == NULL )
                return NULL;
 
        if( (dirp->dd_dirent =
                (struct dirent*)malloc(sizeof(struct dirent))) == NULL )
                return NULL;
 
        dirp->dd_pos = 0;
        strcpy( dirp->dd_name, filename );
 
        return dirp;
 
}
 
struct dirent *
readdir( dirp )
DIR *dirp;
{
static osgbpb_block par;
struct dirent *dp;
char *name;
 
    par.action = 9;
    par.file_handle = (int)dirp->dd_name;
    par.data_addr = dirp->dd_buf;
    par.number = 1;
    par.seq_point = dirp->dd_pos;
    par.buf_len = sizeof( dirp->dd_buf );
    par.wild_fld = "*";
 
    if( osgbpb( &par ) != NULL )
        return NULL;
 
    if( (par.number != 1) || (par.seq_point == -1) )
        return NULL;
 
    name = dirp->dd_buf;
 
    dp = dirp->dd_dirent;
 
    dp->d_namlen = strlen( name );
    dp->d_reclen = sizeof(struct dirent) - (MAXNAMLEN+1) +
                                                  dp->d_namlen + 1;
    strcpy( dp->d_name, name );
 
    dirp->dd_pos++;
 
    return dp;
 
}
 
long
telldir( dirp )
DIR *dirp;
{
        return dirp->dd_pos;
}
 
void
seekdir( dirp, loc )
DIR *dirp;
long loc;
{
        dirp->dd_pos = loc;
        return;
}
 
void
closedir( dirp )
DIR *dirp;
{
        free( dirp->dd_dirent );
        free( dirp );
        return;
}


/* Directory Functions for Unix, written by Jeff Damens, CUCCA, 1984. */
 
/*
 * The path structure is used to represent the name to match.
 * Each slash-separated segment of the name is kept in one
 * such structure, and they are linked together, to make
 * traversing the name easier.
 */
 
struct path {
              char npart[MAXNAMLEN];   /* name part of path segment */
              struct path *fwd;                /* forward ptr */
            };

 
#define SSPACE 2000                     /* size of string-generating buffer */
static char sspace[SSPACE];             /* buffer to generate names in */
static char *freeptr,**resptr;          /* copies of caller's arguments */
static int remlen;                      /* remaining length in caller's array*/
static int numfnd;                      /* number of matches found */

void traverse(struct path *,char *,char *);
int  match(char *,char *);
void addresult(char *);
int  iswild(char *);
 
/*
 * splitpath:
 *  takes a string and splits the slash-separated portions into
 *  a list of path structures.  Returns the head of the list.  The
 *  structures are allocated by malloc, so they must be freed.
 *  Splitpath is used internally by the filename generator.
 *
 * Input: A string.
 * Returns: A linked list of the slash-separated segments of the input.
 */
 
struct path * splitpath(p) char *p;
{
 struct path *head,*cur,*prv;
 int i;
 head = prv = NULL;

 if (*p == ':') p+=2;            
 if (*p==  '.') p++;

 while (*p != '\0')
 {
   cur = (struct path *) malloc(sizeof (struct path));
   if (cur == NULL) {fprintf(stderr,"malloc fails in splitpath()\n");exit(1);}
   cur -> fwd = NULL;
   if (head == NULL) head = cur;
   else prv -> fwd = cur;       /* link into chain */
   prv = cur;

   for (i=0; i < MAXNAMLEN && *p != '.' && *p != '\0'; i++)
     cur -> npart[i] = *p++;
   cur -> npart[i] = '\0';      /* end this segment */
   if (i >= MAXNAMLEN) while (*p != '.' && *p != '\0') p++;
   if (*p == '.') p++;
 }

 return(head);
}


/*
 * fgen:
 *  This is the actual name generator.  It is passed a string,
 *  possibly containing wildcards, and an array of character pointers.
 *  It finds all the matching filenames and stores them into the array.
 *  The returned strings are allocated from a static buffer local to
 *  this module (so the caller doesn't have to worry about deallocating
 *  them); this means that successive calls to fgen will wipe out
 *  the results of previous calls.  This isn't a problem here
 *  because we process one wildcard string at a time.
 *
 * Input: a wildcard string, an array to write names to, the
 *        length of the array.
 * Returns: the number of matches.  The array is filled with filenames
 *          that matched the pattern.  If there wasn't enough room in the
 *         array, -1 is returned.
 * By: Jeff Damens, CUCCA, 1984.
 */
 
int fgen(pat,resarry,len)
char *pat,*resarry[];
int len;
{
 struct path *head;
 char scratch[100],*sptr;

 head = splitpath(pat);
 if (*pat != ':')
 {
  scratch[0] = '\0';
  sptr = scratch;
 }
 else
 {
  strcpy(scratch,pat);
  scratch[3]=0;
  sptr = scratch+3;
 }
                                     /* init buffer correctly */
 numfnd = 0;                           /* none found yet */
 freeptr = sspace;                     /* this is where matches are copied */
 resptr = resarry;                     /* static copies of these so*/
 remlen = len;                         /* recursive calls can alter them */
 traverse(head,scratch,sptr);          /* go walk the directory tree */
 for (; head != NULL; head = head -> fwd)
   free(head);                         /* return the path segments */
 return(numfnd);                       /* and return the number of matches */
}

/* traverse:
 *  Walks the directory tree looking for matches to its arguments.
 *  The algorithm is, briefly:
 *   If the current pattern segment contains no wildcards, that
 *   segment is added to what we already have.  If the name so far
 *   exists, we call ourselves recursively with the next segment
 *   in the pattern string; otherwise, we just return.
 *
 *   If the current pattern segment contains wildcards, we open the name
 *   we've accumulated so far (assuming it is really a directory), then read
 *   each filename in it, and, if it matches the wildcard pattern segment, add
 *   that filename to what we have so far and call ourselves recursively on the
 *   next segment.
 *
 *   Finally, when no more pattern segments remain, we add what's accumulated
 *   so far to the result array and increment the number of matches.
 *
 * Input: a pattern path list (as generated by splitpath), a string
 *       pointer that points to what we've traversed so far (this
 *       can be initialized to "/" to start the search at the root
 *       directory, or to "./" to start the search at the current
 *       directory), and a string pointer to the end of the string
 *       in the previous argument.
 * Returns: nothing.
 */


void
traverse(pl,sofar,endcur)
struct path *pl;
char *sofar,*endcur;
{
 DIR *fd;
 struct dirent dir_entry;
 struct dirent *dirbuf = &dir_entry;
 
 struct FileData fileinfo;
 struct BTim timeinfo;
 int rc;
 int stamped;
 
 if (pl == NULL)
 {
  *--endcur = '\0';                    /* end string, overwrite trailing / */
  addresult(sofar);
  return;
 }
 
 if (!iswild(pl -> npart))
 {
  strcpy(endcur,pl -> npart);
  endcur += strlen(pl -> npart);
  *endcur = '\0';                      /* end current string */
/*  if (stat(sofar,&statbuf) == 0) */      /* if current piece exists */
  rc = GetFileInformation(&fileinfo, &timeinfo, sofar, strlen(sofar));
  if( rc >= 0 )  /* if current piece exists */
  {
      *endcur++ = '.';                  /* add slash to end */
      *endcur = '\0';                  /* and end the string */
      traverse(pl -> fwd,sofar,endcur);
  }
  return;
 }
 
/* segment contains wildcards, have to search directory */
 
 *endcur = '\0'; /* end current string */
 if( *(endcur-1) == '.' )
 {
   /* Stamp on trailing . */
   *--endcur = '\0';
   stamped = 1;
 }
 else
   stamped = 0;
/* if (stat(sofar,&statbuf) == -1) return;*/   /* doesn't exist, forget it */
/* if ((statbuf.st_mode & S_IFDIR) == 0) return;*/  /* not a directory, skip */
 
 if( sofar[0] != '\0' )
 {
  /* cwd is represented as a NULL string!! */
  rc = GetFileInformation(&fileinfo, &timeinfo, sofar, strlen(sofar));
  if( rc == -1 ) return;   /* doesn't exist, forget it */
  if( rc != 2 ) return;    /* not a directory, skip */
 }
 
 if ((fd = opendir(sofar)) == NULL) return;    /* can't open, forget it */
 
 if( stamped != 0 )
 {
   *endcur++ = '.';
   *endcur = '\0';
 }
 
 while ( (dirbuf = readdir(fd)) != NULL )
 {
  strncpy(nambuf,dirbuf->d_name,MAXNAMLEN); /* Get a null terminated copy!!! */
  nambuf[MAXNAMLEN] = '\0';
/*  if (dirbuf->d_ino != 0 && match(pl -> npart,nambuf)) {*/
  if ( match(pl -> npart,nambuf)) {
    char *eos;
    strcpy(endcur,nambuf);
    eos = endcur + strlen(nambuf);
    *eos = '.';                    /* end this segment */
    traverse(pl -> fwd,sofar,eos+1);
 }
}
 
 closedir(fd);
}
/*
 * addresult:
 *  Adds a result string to the result array.  Increments the number
 *  of matches found, copies the found string into our string
 *  buffer, and puts a pointer to the buffer into the caller's result
 *  array.  Our free buffer pointer is updated.  If there is no
 *  more room in the caller's array, the number of matches is set to -1.
 * Input: a result string.
 * Returns: nothing.
 */
 
void addresult(str)
char *str;
{
 int l;
 if (strncmp(str,"./",2) == 0) str += 2;
 if (--remlen < 0) {
  numfnd = -1;
  return;
 }
 l = strlen(str) + 1;                  /* size this will take up */
 if ((freeptr + l) > &sspace[SSPACE-1]) {
    numfnd = -1;                       /* do not record if not enough space */
    return;
  }
 strcpy(freeptr,str);
 *resptr++ = freeptr;
 freeptr += l;
 numfnd++;
}

 
int iswild(str)
char *str;
{
 char c;
 while ((c = *str++) != '\0')
   if (c == '*' || c == '?') return(1);
 return(0);
}

/*
 * match:
 *  pattern matcher.  Takes a string and a pattern possibly containing
 *  the wildcard characters '*' and '?'.  Returns true if the pattern
 *  matches the string, false otherwise.
 * by: Jeff Damens, CUCCA
 *
 * Input: a string and a wildcard pattern.
 * Returns: 1 if match, 0 if no match.
 */
 
int match(pattern,string) char *pattern,*string; {
    char *psave,*ssave;           /* back up pointers for failure */
    psave = ssave = NULL;
    while (1) {
       for (; toupper(*pattern)==toupper(*string); pattern++,string++)
                                               /* skip first */
           if (*string == '\0') return(1);     /* end of strings, succeed */
       if (*string != '\0' && *pattern == '?') {
           pattern++;                  /* '?', let it match */
           string++;
       } else if (*pattern == '*') {   /* '*' ... */
           psave = ++pattern;          /* remember where we saw it */
           ssave = string;             /* let it match 0 chars */
       } else if (ssave != NULL && *ssave != '\0') {   /* if not at end  */
                                       /* ...have seen a star */
           string = ++ssave;           /* skip 1 char from string */
           pattern = psave;            /* and back up pattern */
       } else return(0);               /* otherwise just fail */
    }
}



/*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
/*
  Returns the number of files that match fn1, with data structures set up
  so that first file (if any) will be returned by the next znext() call.
*/
int zxpand(fn) char *fn;
{
  fcount = fgen(fn,mtchs,MAXWLD);    /* Look up the file. */
  if (fcount > 0)
  {
     mtchptr = mtchs;                /* Save pointer for next. */
  }
  return(fcount);
}
 
 
/*  Z N E X T  --  Get name of next file from list created by zxpand(). */
/*
 Returns >0 if there's another file, with its name copied into the arg string,
 or 0 if no more files in list.
*/
int znext(fn) char *fn; {
    if (fcount-- > 0) strcpy(fn,*mtchptr++);
    else *fn = '\0';
    return(fcount+1);
}

